﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

using SpectationClient.Stuff;

namespace SpectationClient.SpectralTools {
   
    /// <summary>
    /// Class to write ENVI Spectral Libraries from different spectral input_y sources
    /// </summary>
    public class ESL_Writer {

        /// <summary>
        /// Constructor to create an ESL_Writer
        /// </summary>
        public ESL_Writer() { }
       
        public void createESLFiles(FileInfo fileInfo, List<Spectrum> spectra, List<String> spectraNames=null) {
            //separate spectra by its definition of bands
            // -> disjunct by number of bands and band wavelengths
            List<Tuple<String, Spectrum>> specList = new List<Tuple<string, Spectrum>>();
            
            if(spectraNames != null){
                if(spectra.Count != spectraNames.Count) {
                    throw new Exception("The lists 'spectra' and 'spectraNames' must have the same number of elements"); 
                }
            }else{
                spectraNames = new List<string>();
                for(int i = 0; i < spectra.Count; i++) {
                    spectraNames.Add(spectra[i] != null && !String.IsNullOrWhiteSpace(spectra[i].SpectrumName) ? 
                        spectra[i].SpectrumName : null);
                }
            }


            for(int i = 0; i < spectra.Count; i++) {
                if(spectra[i] != null) {
                    specList.Add(new Tuple<String, Spectrum>(spectraNames[i], spectra[i]));
                }
            }

            var queryGroups = from tupel in specList
                             group tupel by tupel.Item2.NumberOfBands into newGroup
                             select newGroup;



            int cnt = 0;
            foreach(var group in queryGroups) {
                List<Spectrum> groupSpectra = group.Select(p => p.Item2).ToList();
                List<String> groupSpectraNames = group.Select(p => p.Item1).ToList();
                String strHeader = getHeaderFileText(groupSpectra, groupSpectraNames);
                Byte[] BA = getMainFile(groupSpectra);
              
                
                FileInfo fileInfo2 = DataHelper.createNewFilePath(fileInfo, ref cnt);
                
                FileStream fs1 = File.Create(fileInfo2.FullName);
                fs1.Write(BA, 0, BA.Length);

                fs1.Flush();
                fs1.Close();
                String nameHeaderFile = getHeaderPath(fileInfo2);
                StreamWriter fs2 = File.CreateText(nameHeaderFile);
                fs2.Write(strHeader);
                fs2.Close();
            }

        }


        private String getHeaderPath(FileInfo fileInfo) { 
            String basename = fileInfo.FullName;
            if(!String.IsNullOrEmpty(fileInfo.Extension)){
                basename = Regex.Replace(fileInfo.FullName, @"\.[^.]+$", "", RegexOptions.IgnoreCase);
            
            }
            return basename + ".hdr";

        }

        /// <summary>
        /// Returns the header text as byte array with ASCII encodings.
        /// </summary>
        /// <param name="spectra"></param>
        /// <param name="spectranames"></param>
        /// <returns></returns>
        public Byte[] getHeaderFileBytes(IList<Spectrum> spectra, IList<String> spectranames = null) {
            String headerText = this.getHeaderFileText(spectra, spectranames);
            Byte[] b = ASCIIEncoding.ASCII.GetBytes(headerText);

            String hdr2 =  ASCIIEncoding.ASCII.GetString(b);
            return ASCIIEncoding.ASCII.GetBytes(headerText);
            
        }

        /// <summary>
        /// Returns the header text
        /// </summary>
        /// <param name="spectra"></param>
        /// <param name="spectranames"></param>
        /// <returns></returns>
        public String getHeaderFileText(IList<Spectrum> spectra, IList<String> spectranames = null) {
            if (spectra.Count == 0) return null;
            
            //check the spectra
            Single[] ch = spectra[0].Bands;
            String[] bbl = null;
            if(spectra[0].BadBands != null) {
                bbl = (from b in (spectra[0].BadBands)
                                select (b ? "1":"0")).ToArray();
            }

            List<String> spectracomments = new List<string>();
            if(spectranames == null) {
                spectranames = new List<string>();
                for(int i = 0; i < spectra.Count; i++) {
                    String specName = spectra[i].SpectrumName;
                    if(specName.Trim().Length == 0) specName = String.Format("Spectrum{0}", i+1);
                    spectranames.Add(specName);
                    String comment = spectra[i].Comments;
                    if(comment != null) spectracomments.Add(comment);
                }
            } 
            


            StringBuilder SB = new StringBuilder();
            SB.AppendLine("ENVI");
            SB.AppendLine("samples = " + ch.Length);
            SB.AppendLine("interleave = bsq");
            SB.AppendLine("byte order = 0");
            SB.AppendLine("lines = " + spectra.Count);
            SB.AppendLine("bands = 1");
            SB.AppendLine("header offset = 0");
            SB.AppendLine("file type = ENVI Spectral Library");
            String nl = Environment.NewLine;
            SB.AppendLine("data type = 4");//5 = Double 
            SB.AppendFormat("spectra names = {{{0}}}\n", TextHelper.combine<String>(spectranames, ",", 1));
            SB.AppendFormat("wavelength = {{{0}\n}}\n", TextHelper.combine<Single>(ch, ", ", 10));
            SB.AppendFormat("wavelength units = {0}\n", "nanometers");
            if(bbl != null){
                SB.AppendFormat("bbl = {{{0}\n}}\n", TextHelper.combine<String>(bbl, ", ", 20));
            }
            SB.AppendFormat("description = {{{0}}}\n", TextHelper.combine<String>(spectracomments, ", ", 1));

            return SB.ToString();
        }

        public Byte[] getMainFile(IList<Spectrum> spectra) {
            if (spectra.Count == 0) return null;
            long l = spectra[0].NumberOfBands;
            Byte[] byteValues = new Byte[l * spectra.Count * 4];
            BinaryBuilder builder = BinaryBuilder.CreateBinaryBuilder(byteValues);
            
            for(int iSpectrum = 0; iSpectrum < spectra.Count; iSpectrum++) {
                Spectrum spec = spectra[iSpectrum];
                if(spec.NumberOfBands != l) throw new Exception("Error ESL_Writer: different number of channels");
                builder.WriteArray(spec.BandValues);
            }
            return byteValues;
        }
   
    }
}
